/**
 * @author erond
 */

package master;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.sql.Timestamp;
import java.util.NoSuchElementException;
import java.util.Observable;
import java.util.StringTokenizer;
import javax.swing.event.EventListenerList;
import commonUtility.FtpClientThread;
import commonUtility.LineUtility;
import commonUtility.SendRssiThread;
import commonUtility.XmlMerge;
import btInquiryRssi.BtInquiryRssi;
import btInquiryRssi.InquiryThread;

/**
 * MasterGatewayServerThread è la classe che genera un thread in grado di implementare le principali funzioni del gateway
 * a parte l'invio dei contenuti multimediali ai client.
 * In particolare si occupa di gestire la comunicazione tramite LAN con lo SLAVE locale, inviare tramite Bluetooth ai client rilevati in inquiry
 * il valore di rssi rilevato per loro, e il nome del file (punto di interesse) proprio del gateway, utile al client
 * ai fini della localizzazione sulla mappa.
 * Inoltre si informa sullo stato dei vicini ed attua la funzione di LoadBalancing per l'eventuale usurpo di stream ai gateway vicini.
 */
public class MasterJobServer extends Observable implements Runnable {

	private Socket slaveClientSocket = null;
	/**
	 * @return the slaveClientSocket
	 */
	public Socket getSlaveClientSocket() {
		return slaveClientSocket;
	}

	//TODO settare qui il timeToSleep per rallentare il flusso di esecuzione
	private final static int timeToSleep = 0;
	private ServerSocket masterJobServerSocket = null;
	private String lastSignalSend="";
	private String lastSignalReceived="";
	private String consoleMessage="";
	private InetAddress beforeGatewayMulticastIP = null;
	private InetAddress afterGatewayMulticastIP = null;
	private int beforeGatewayMulticastPort;
	private int afterGatewayMulticastPort;
	private int inquiryMaxTime;
	/**
	 * @return the inquiryMaxTime
	 */
	public int getInquiryMaxTime() {
		return inquiryMaxTime;
	}


	/**
	 * @return the inquiryMaxPacket
	 */
	public int getInquiryMaxPacket() {
		return inquiryMaxPacket;
	}

	private int inquiryMaxPacket;
	private InetAddress slaveMulticastIP;
	private int slaveMulticastPort;
	private boolean run = false;
	private int hciDev;


	/**
	 * @return the hciDev
	 */
	public int getHciDev() {
		return hciDev;
	}


	/**
	 * @return the run
	 */
	public boolean isRun() {
		return run;
	}


	/**
	 * @param run the run to set
	 */
	public void setRun(boolean run) {
		this.run = run;
	}


	/**
	 * @return the lastSignalSend
	 */
	public String getLastSignalSend() {
		return lastSignalSend;
	}


	/**
	 * @return the lastSignalReceived
	 */
	public String getLastSignalReceived() {
		return lastSignalReceived;
	}


	/**
	 * @return the masterJobServerSocket
	 */
	public ServerSocket getMasterJobServerSocket() {
		return masterJobServerSocket;
	}


	private BeforeGatewayLink beforeGatewayLink = null;
	/**
	 * @return the beforeGatewayLink
	 */
	public BeforeGatewayLink getBeforeGatewayLink() {
		return beforeGatewayLink;
	}


	public AfterGatewayLink afterGatewayLink = null;
	/**
	 * @return the afterGatewayLink
	 */
	public AfterGatewayLink getAfterGatewayLink() {
		return afterGatewayLink;
	}

	private BeforeGatewayLink.BeforeGatewayLink_ClientOutput before = null;
	private AfterGatewayLink.AfterGatewayLink_ClientOutput after = null;

	private InquiryThread inquiryThread = null;
	/**
	 * @return the inquiryThread
	 */
	public InquiryThread getInquiryThread() {
		return inquiryThread;
	}


	private BtInquiryRssi pojo = null;
	/**
	 * @return the iBluetooth
	 */
	public BtInquiryRssi getPojo() {
		return pojo;
	}

	private InetAddress slaveIP = null;
	/**
	 * @return the slaveIP
	 */
	public InetAddress getSlaveIP() {
		return slaveIP;
	}

	private String networkInterface = null;
	public static EventListenerList listenerList = null;
	public boolean beforeFlag = false;
	public boolean afterFlag = false;
	public MasterMonitor masterMonitor = null;
	private boolean enabledBefore = true;
	private boolean enabledAfter = true;
	private Object semaphore = new Object();
	private String defaultFileName = "";
	private SendRssiThread sendRssiThread = null;
	private int inquiryThresholding = 0;
	private Object runSemaphore = new Object();
	private Object inquiryValuesSemaphore = new Object();


	/**
	 * @return the inquiryValuesSemaphore
	 */
	public Object getInquiryValuesSemaphore() {
		return inquiryValuesSemaphore;
	}


	/**
	 * @return the runSemaphore
	 */
	public Object getRunSemaphore() {
		return runSemaphore;
	}


	/**
	 * @return the inquiryThresholding
	 */
	public int getInquiryThresholding() {
		return inquiryThresholding;
	}


	/**
	 * @param inquiryMaxTime the inquiryMaxTime to set
	 */
	public void setInquiryMaxTime(int inquiryMaxTime) {
		this.inquiryMaxTime = inquiryMaxTime;
	}


	/**
	 * @param inquiryMaxPacket the inquiryMaxPacket to set
	 */
	public void setInquiryMaxPacket(int inquiryMaxPacket) {
		this.inquiryMaxPacket = inquiryMaxPacket;
	}


	/**
	 * @param inquiryThresholding the inquiryThresholding to set
	 */
	public void setInquiryThresholding(int inquiryThresholding) {
		this.inquiryThresholding = inquiryThresholding;
	}


	/**
	 * @return the defaultFileName
	 */
	public String getDefaultFileName() {
		return defaultFileName;
	}


	/**
	 * @param defaultFileName the defaultFileName to set
	 */
	public void setDefaultFileName(String defaultFileName) {
		this.defaultFileName = defaultFileName;
	}


	/**
	 * @return the enabledBefore
	 */
	public boolean isEnabledBefore() {
		return enabledBefore;
	}


	/**
	 * @param enabledBefore the enabledBefore to set
	 */
	public void setEnabledBefore(boolean enabledBefore) {
		this.enabledBefore = enabledBefore;
	}


	/**
	 * @return the enabledAfter
	 */
	public boolean isEnabledAfter() {
		return enabledAfter;
	}


	/**
	 * @param enabledAfter the enabledAfter to set
	 */
	public void setEnabledAfter(boolean enabledAfter) {
		this.enabledAfter = enabledAfter;
	}

	//posizione del flusso di esecuzione
	private int position = -1;


	/**
	 * @return the position
	 */
	public int getPosition() {
		return position;
	}


	/**
	 * @return the beforeFlag
	 */
	public boolean getBeforeFlag() {
		return beforeFlag;
	}

	/**
	 * @param beforeFlag the beforeFlag to set
	 */
	public void setBeforeFlag(boolean beforeFlag) {
		this.beforeFlag = beforeFlag;
	}

	/**
	 * @return the afterFlag
	 */
	public boolean getAfterFlag() {
		return afterFlag;
	}

	/**
	 * @param afterFlag the afterFlag to set
	 */
	public void setAfterFlag(boolean afterFlag) {
		this.afterFlag = afterFlag;
	}

	/**
	 * @return the consoleMessage
	 */
	public String getConsoleMessage() {
		return consoleMessage;
	}


	/**
	 * @param afterGatewayMulticastPort the afterGatewayMulticastPort to set
	 */
	public void setAfterGatewayMulticastPort(int afterGatewayMulticastPort) {
		this.afterGatewayMulticastPort = afterGatewayMulticastPort;
	}


	/**
	 * @return the afterGatewayMulticastPort
	 */
	public int getAfterGatewayMulticastPort() {
		return afterGatewayMulticastPort;
	}


	/**
	 * @param pojo Oggetto BtInquiryRssi rappresentate l'interfaccia IBluetooth da noi dotata di libreria per la cattura degli rssi da utilizzare per l'inquiry
	 * @param hciDev Dispositivo hci bluetooth da utilizzare
	 * @param masterJobServerSocket Server Socket sulla quale il MasterJobServer sarà in ascolto per la comunicazione con lo Slave
	 * @param beforeGatewayLinkThread Riferimento al thread di comunicazione con il vicino BEFORE
	 * @param afterGatewayLinkThread Riferimento al thread di comunicazione con il vicino AFTER
	 * @param slaveIP Indirizzo IP dello Slave
	 * @param monitor Oggetto Observer monitor di sistema al quale inviare notifiche notifyObserver
	 * @param enableBefore Booleano utilizzato per i test in locale che disabilita lo scaricamento del file di stato xml del vicino BEFORE
	 * @param enableAfter Booleano utilizzato per i test in locale che disabilita lo scaricamento del file di stato xml del vicino AFTER
	 * @param beforeMulticastIP Indirizzo IP di multicast del vicino BEFORE sul quale viene pubblicato il file di stato xml del relativo gateway
	 * @param afterMulticastIP Indirizzo IP di multicast del vicino AFTER sul quale viene pubblicato il file di stato xml del relativo gateway
	 * @param beforeMulticastPort Porta sulla quale il server multicast del vicino BEFORE effettua il servizio di pubblicazione del file di stato xml
	 * @param afterMulticastPort Porta sulla quale il server multicast del vicino AFTER effettua il servizio di pubblicazione del file di stato xml
	 * @param inquiryMaxTime Limite di tempo massimo dell'inquiry
	 * @param inquiryMaxPacket Limite di pacchetti da catturare durante l'inquiry
	 * @param inquiryThresholding Limite di RSSI oltre il quale effettuare l'eventuale filtraggio dei dispositivi rilevati
	 * @param slaveMultiIP Indirizzo IP di multicast sul quale lo Slave locale pubblica il file xml rappresentate lo stato interno del gateway
	 * @param slaveMultiPort Porta sulla quale il server multicast dello slave pubblica il file gateway.xml
	 * @param networkInterface Interfaccia di rete da utilizzare per la comunicazione con lo slave e con i vicini (eth0, ath0, wifi0, ecc...)
	 * @param defaultFileName Nome del file multimediale da inviare ai client di default
	 */
	public MasterJobServer(BtInquiryRssi pojo, int hciDev, ServerSocket masterJobServerSocket,
			BeforeGatewayLink beforeGatewayLinkThread,AfterGatewayLink afterGatewayLinkThread,
			InetAddress slaveIP, MasterMonitor monitor, boolean enableBefore, boolean enableAfter,
			InetAddress beforeMulticastIP, InetAddress afterMulticastIP, int beforeMulticastPort,
			int afterMulticastPort, int inquiryMaxTime, int inquiryMaxPacket, int inquiryThresholding,
			InetAddress slaveMultiIP, int slaveMultiPort, String networkInterface, String defaultFileName) {

		this.masterJobServerSocket = masterJobServerSocket;
		this.beforeGatewayLink = beforeGatewayLinkThread;
		this.afterGatewayLink = afterGatewayLinkThread;
		this.pojo = pojo;
		this.hciDev=hciDev;
		before=beforeGatewayLinkThread.getBeforeGatewayLink_ClientOutput();
		after=afterGatewayLinkThread.getAfterGatewayLink_ClientOutput();
		this.slaveIP=slaveIP;
		masterMonitor=monitor;
		this.addObserver(masterMonitor);
		this.enabledBefore=enableBefore;
		this.enabledAfter=enableAfter;
		this.beforeGatewayMulticastIP=beforeMulticastIP;
		this.beforeGatewayMulticastPort=beforeMulticastPort;
		this.afterGatewayMulticastIP=afterMulticastIP;
		this.afterGatewayMulticastPort=afterMulticastPort;
		this.inquiryMaxTime=inquiryMaxTime;
		this.inquiryMaxPacket=inquiryMaxPacket;
		this.slaveMulticastIP=slaveMultiIP;
		this.slaveMulticastPort=slaveMultiPort;
		this.networkInterface=networkInterface;
		this.defaultFileName=defaultFileName;
		this.inquiryThresholding=inquiryThresholding;
	}


	/**
	 * @return the networkInterface
	 */
	public String getNetworkInterface() {
		return networkInterface;
	}

	//metodo per lanciare il thread
	public void run() {

		inquiryThread = new InquiryThread(inquiryMaxTime, inquiryMaxPacket, inquiryThresholding, pojo);

		Timestamp timestamp = new Timestamp(System.currentTimeMillis());
		consoleMessage="["+timestamp+"] MasterJobServerThread avviato...";
		setChanged();
		notifyObservers(this);


		System.out.println(consoleMessage);

		beforeGatewayLink.setSemaphore(semaphore);
		beforeGatewayLink.setMasterJobServer(this);
		afterGatewayLink.setSemaphore(semaphore);
		afterGatewayLink.setMasterJobServer(this);


		try {
			/**
			 * Il Thread,una volta creata la socket si pone in attesa che la connessione
			 * venga accettata anche lato client. Attende inoltre che venga effettuata una richiesta
			 * di servizio. BLOCCANTE!!!
			 */
			slaveClientSocket=masterJobServerSocket.accept();
			System.out.println("MasterJobServerThread: sbloccato dalla accept");
			consoleMessage="MasterJobServerThread: sbloccato dalla accept";

			setChanged();
			notifyObservers(this);
			masterJobServerSocket.setSoTimeout(60000);
		}
		catch (IOException ioe) {
			System.out
			.println("MasterJobServerThread:Problemi nella masterGatewayServerSocket.accept() "
					+ "su socket: "+masterJobServerSocket);
			ioe.printStackTrace();
			return;
		}
		catch (Exception e) {
			//generica eccezione
			System.out.println("MasterJobServerThread: ECCEZIONE "+"su socket: "+slaveClientSocket);
			e.printStackTrace();
		}

		DataInputStream inSock = null;
		DataOutputStream outSock = null;

		try {
			// creazione stream di input e out da socket
			inSock = new DataInputStream(slaveClientSocket.getInputStream());
			outSock = new DataOutputStream(slaveClientSocket.getOutputStream());
		}
		catch (IOException ioe) {
			System.out
			.println("MasterJobServerThread: Problemi nella creazione degli stream di input/output "
					+ "su socket: "+slaveClientSocket);
			ioe.printStackTrace();
			return;
		}

		int kk=0;

		boolean ciclo = true;
		while(ciclo) {

			/**
			 * Controllo se da console è stato premuto il pulsante run/pause. Nel caso mi metto in wait
			 */
			if (!isRun()) {
				synchronized (getRunSemaphore()) {
					try {
						getRunSemaphore().wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}

			try {
				position = 0;
				setChanged();
				notifyObservers(this);

				/**
				 * Avvio il thread che si occupa dell'inquiry ed attendo che termini, fornendo il risultato
				 * inquiry.txt
				 */
				synchronized(getInquiryValuesSemaphore()) {
					inquiryThread = new InquiryThread(inquiryMaxTime, inquiryMaxPacket, inquiryThresholding, pojo);
				}
				inquiryThread.start();
				try {
					lastSignalReceived="INQUIRING";
					setChanged();
					notifyObservers(this);

					inquiryThread.join(25000);
					try {
						inquiryThread.interrupt();
					} catch (SecurityException se) {
						se.printStackTrace();
						System.err.println("Eccezione nella interrupt thread!");
					}
				} catch (InterruptedException ie_inq) {
					System.err.println("Eccezione nell'inquiry thread!");
					ie_inq.printStackTrace();
				}

				timestamp = new Timestamp(System.currentTimeMillis());
				consoleMessage="MASTERJOBSERVERTHREAD: Inquiry thread terminato, si comincia!";
				System.out.println("["+timestamp+"] "+consoleMessage);
				position++;
				setChanged();
				notifyObservers(this);

				/**
				 * Avvio il thread che informa i client rilevati in inquiry dell'RSSI con il quale sono stati trovati
				 */
				sendRssiThread = new SendRssiThread(getDefaultFileName(),getHciDev());
				sendRssiThread.start();

				/**
				 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
				 */
				Thread.sleep(timeToSleep);

				/**
				 * prima di inviare il file allo slave si è pensato di filtrarlo dei client più lontani
				 * di inquiryThresholding RSSI!
				 */
				filterInquiryWithThresholding(getInquiryThresholding(),"./gatewayTmp/inquiryTmp.txt", "./gatewayTmp/inquiry.txt");

				/**
				 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI
				 */
				Thread.sleep(timeToSleep);


				/**
				 * 1) trasferimento del file inquiry.txt
				 */
				FtpClientThread inquiry = new FtpClientThread(slaveIP, 2122, "./gatewayTmp/inquiry.txt", "inquiry.txt");
				inquiry.setName("masterJobServer_ftpClientThread_inquirySend");
				inquiry.start();


				//Attendo che il trasferimento sia completato
				try {
					inquiry.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				position++;
				setChanged();
				notifyObservers(this);


				/**
				 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
				 */
				Thread.sleep(timeToSleep);

				String signal = null;

				while(true) {
					try {
						outSock.writeUTF("INQUIRYREADY");
						lastSignalSend="INQUIRYREADY";
						position++;
						setChanged();
						notifyObservers(this);

						/**
						 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
						 */
						Thread.sleep(timeToSleep);
						while(inSock.available()>=0) {
							signal=inSock.readUTF();
							if(signal.equalsIgnoreCase("INQUIRYREADYRECEIVED")) {
								lastSignalReceived=signal;
								System.err.println(Thread.currentThread().getName()+": Ricevuto "+signal+" ..continuo!");
								consoleMessage=Thread.currentThread().getName()+": Ricevuto "+signal+" ..continuo!";
								position++;
								setChanged();
								notifyObservers(this);

								/**
								 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
								 */
								Thread.sleep(timeToSleep);
								//								}
								/**
								 * se ricevo questa stringa vuol dire che lo slave ha ricevuto INQUIRYREADY
								 */
								break;
							}
						}
						break;
					} catch (IOException e) {
						e.printStackTrace();
					}
				}

				try {
					outSock.flush();
				} catch (IOException ioe) {
					System.err.println(Thread.currentThread().getName()+": Eccezione nella flush");
					ioe.printStackTrace();
				}

				/**
				 * 2) Attesa del segnale OKDOWNLOADXML da parte dello SLAVE e conseguente download dell'xml
				 */

				boolean flag_down_xml=true;
				String tmp = null;

				while(flag_down_xml) {
					try{
						//Attendo di ricevere dati sulla socket
						if(inSock.available()>=0) {
							tmp=inSock.readUTF();
							//controllo la semantica del dato ricevuto
							if (tmp.contains("OKDOWNLOADXML")) {
								lastSignalReceived=tmp;
								position++;
								setChanged();
								notifyObservers(this);

								/**
								 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
								 */
								Thread.sleep(timeToSleep);

								MulticastClient xmlGateway = new MulticastClient(slaveMulticastIP,slaveMulticastPort,"./gatewayTmp/",0,false);
								xmlGateway.start();
								xmlGateway.join();
								flag_down_xml=false;

								outSock.writeUTF("GRAZIE");
								lastSignalSend="GRAZIE";
								System.out.println("MasterJobServerThread: invio allo slave la stringa GRAZIE");
								consoleMessage="MasterJobServerThread: invio allo slave la stringa GRAZIE";
								position++;
								setChanged();
								notifyObservers(this);
								/**
								 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
								 */
								Thread.sleep(timeToSleep);
							}  
						}//fine if  

					} catch (SocketException se) {
						se.printStackTrace();
						try {
							inSock.close();
							outSock.close();
							slaveClientSocket.close();
							System.exit(0);
						} catch (IOException e) {
							e.printStackTrace();
							System.exit(1);
						}
					}
					catch (IOException ioe) {
						System.err.println("\nMasterJobServerThread: Lo SLAVE ha chiuso la connessione. Esco e chiudo\n");
						System.exit(99);
						break;
					}


				}//fine while(flag_down_xml)

				try {
					outSock.flush();
				} catch (IOException ioe) {
					System.err.println(Thread.currentThread().getName()+": Eccezione nella flush");
					ioe.printStackTrace();
				}

				/**
				 * 3) Devo avviare il download degli xml dei vicini
				 */

				MulticastClient xmlBeforeGateway = null;
				MulticastClient xmlAfterGateway = null;

				System.out.println("MasterJobServerThread: creo i client multicast per lo scaricamento degli xml dei vicini..");
				consoleMessage="MasterJobServerThread: creo i client multicast per lo scaricamento degli xml dei vicini..";
				setChanged();
				notifyObservers(this);


				if (enabledBefore) {
					xmlBeforeGateway = new MulticastClient(this.beforeGatewayMulticastIP,beforeGatewayMulticastPort,"./gatewayTmp/before_",1000,false);
					xmlBeforeGateway.start();
					position++;
					setChanged();
					notifyObservers(this);

					try {
						/**
						 * Per ovviare a problemi sul gateway BEFORE il thread viene terminato dopo 20 secondi.
						 * In questo caso viene utilizzata l'ultima copia scaricata del file
						 */
						xmlBeforeGateway.join(20000);
					} catch (InterruptedException e) {
						System.out.println("Eccezione nella join del xmlBeforeGatewayThread e/o scattato il timeout di 20 secondi");
						e.printStackTrace();
						xmlBeforeGateway.interrupt();
					}
				} else {
					/**
					 * Simulo il download per il flusso di esecuzione
					 */
					position++;
					setChanged();
					notifyObservers(this);
				}

				/**
				 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
				 */
				Thread.sleep(timeToSleep);

				if (enabledAfter) {
					xmlAfterGateway = new MulticastClient(this.afterGatewayMulticastIP,afterGatewayMulticastPort,"./gatewayTmp/after_",1000,false);
					xmlAfterGateway.start(); 
					position++;
					setChanged();
					notifyObservers(this);

					try {
						/**
						 * Per ovviare a problemi sul gateway AFTER il thread viene terminato dopo 20 secondi.
						 * In questo caso viene utilizzata l'ultima copia scaricata del file
						 */
						xmlAfterGateway.join(20000);
					} catch (InterruptedException e) {
						System.out.println("Eccezione nella join del xmlAfterGatewayThread e/o scattato il timeout di 20 secondi");
						e.printStackTrace();
						xmlAfterGateway.interrupt();
					}
				} else {
					/**
					 * Simulo il download per il flusso di esecuzione
					 */
					position++;
					setChanged();
					notifyObservers(this);
				}

				/**
				 * 4) Avvio il protocollo di LoadBalancing. Al termine avrò pronto il file job.xml
				 */

				String nomeFile = "./gatewayTmp/job.xml";
				String nomeFileTmp = "./gatewayTmp/jobTmp.xml";

				/**
				 * Cancello il contenuto vecchio dei file job.xml e jobTmp.xml
				 */
				commonUtility.XmlHandler.makeXml("",nomeFile, networkInterface);
				commonUtility.XmlHandler.makeXml("",nomeFileTmp, networkInterface);

				/**
				 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
				 */
				Thread.sleep(timeToSleep);

				position++;
				//	System.out.println("DEBUG: sono prima delle LoadBalancing Before. position="+position);
				setChanged();
				notifyObservers(this);

				LoadBalancing loadBalancingBefore = new LoadBalancing("./gatewayTmp/before_", "./gatewayTmp/gateway.xml", nomeFileTmp, true, getDefaultFileName());
				loadBalancingBefore.start();
				loadBalancingBefore.join();

				/**
				 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
				 */
				Thread.sleep(timeToSleep);

				position++;
				//	System.out.println("DEBUG: sono prima delle LoadBalancing After. position="+position);
				setChanged();
				notifyObservers(this);

				LoadBalancing loadBalancingAfter = new LoadBalancing("./gatewayTmp/after_", nomeFileTmp , nomeFile, false, getDefaultFileName());
				loadBalancingAfter.start();
				loadBalancingAfter.join();

				kk++;

				beforeFlag=false;
				afterFlag=false;

				int k=0;

				/**
				 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
				 */
				Thread.sleep(timeToSleep);

				synchronized (before) {
					if ((LineUtility.linesNumber("./gatewayTmp/before_thief.txt"))>0) {
						timestamp = new Timestamp(System.currentTimeMillis());
						consoleMessage="["+timestamp+"] HO TROVATO job da poter rubare al before gateway!";
						System.err.println(consoleMessage);				
						before.setMasterJobServerThread(this);
						before.notify();
						beforeFlag=true;
						k++;
					}	else {
						consoleMessage="Non ho trovato job da poter rubare al before gateway";
						System.err.println(consoleMessage);

					}
					position++;
					System.out.println("DEBUG: Ho controllato se dovrò usurpare al Before (beforeFlag="+beforeFlag+"). position="+position);
					setChanged();
					notifyObservers(this);

				}
				synchronized (after) {
					if ((LineUtility.linesNumber("./gatewayTmp/after_thief.txt"))>0) {
						timestamp = new Timestamp(System.currentTimeMillis());
						consoleMessage="["+timestamp+"] HO TROVATO job da poter rubare all' after gateway!";
						System.err.println(consoleMessage);
						after.setMasterJobServerThread(this);
						after.notify();
						afterFlag=true;
						k++;
					} else {
						consoleMessage="Non ho trovato job da poter rubare all' after gateway";
						System.err.println(consoleMessage);
					}
					position++;
					System.out.println("DEBUG: Ho controllato se dovrò usurpare all' After (afterFlag="+afterFlag+"). position="+position);
					setChanged();
					notifyObservers(this);
				}

				/**
				 * se ho sbloccato i thread e quindi i file *_thief.txt contengono qualcosa
				 * allora devo attendere che i thread sbloccati terminino la loro esecuzione.
				 * Solo allora infatti loro avranno ricevuto gli eventuali got_after(o before)_transfer_job.xml!
				 * e in qualche modo devono notificarmelo... (this.wait e loro fanno notify?)
				 */

				synchronized (semaphore) {
					if(k>0) {
						System.out.println("MasterJobServerThread: devo attendere che il/i "+k+" thread sbloccato/i di comunicaz con i vicini terminino la loro esecuzione");
						try {
							semaphore.wait();
						} catch (InterruptedException ie) {
							ie.printStackTrace();
							continue;
						}
						System.out.println(Thread.currentThread().getName()+": sbloccato, controllo i flag..");
						if ((beforeFlag==false) && (afterFlag==false)) {
							System.out.println("beforeFlag="+beforeFlag+", afterFlag="+afterFlag+" quindi posso proseguire!");
						} else k--;		
					}
				}

				consoleMessage="MasterJobServerThread: Posso fare il merge degli xml";
				System.out.println(consoleMessage);

				position++;//13
				setChanged();
				notifyObservers(this);

				/**
				 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
				 */
				Thread.sleep(timeToSleep);

				position++;//14
				setChanged();
				notifyObservers(this);

				XmlMerge xmlMerge = new XmlMerge("./gatewayTmp/job.xml","./gatewayTmp/got_after_transfer_job.xml","./gatewayTmp/got_before_transfer_job.xml");
				xmlMerge.start();

				xmlMerge.join();

				/**
				 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
				 */
				Thread.sleep(timeToSleep);

				position++;//15
				setChanged();
				notifyObservers(this);
				/**
				 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
				 */
				Thread.sleep(timeToSleep);

				/**
				 * 5) Devo fare un merge degli EVENTUALI got_after(o before)_transfer_job.xml ricevuti, copiandone
				 *    i valore in job.xml, da inviare poi allo SLAVE, come lista finale di JOB da attivare. FINE.
				 */

				FtpClientThread job = new FtpClientThread(slaveIP, 2122, "./gatewayTmp/job.xml", "job.xml");
				job.setName("masterJobServerThread_ftpClientThread_jobSend");
				job.start();

				//Attendo che il trasferimento sia completato
				try {
					job.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}


				try {
					outSock.flush();
				} catch (IOException ioe) {
					System.err.println(Thread.currentThread().getName()+": Eccezione nella flush");
					ioe.printStackTrace();
				}

				commonUtility.XmlHandler.makeXml("","./gatewayTmp/got_after_transfer_job.xml", networkInterface);
				commonUtility.XmlHandler.makeXml("","./gatewayTmp/got_before_transfer_job.xml", networkInterface);


				while(true) {
					try {
						outSock.writeUTF("JOBREADY");
						lastSignalSend="JOBREADY";
						position++;//16
						setChanged();
						notifyObservers(this);

						while(inSock.available()>=0) {
							signal=inSock.readUTF();
							if(signal.equalsIgnoreCase("JOBREADYRECEIVED")) {
								lastSignalReceived=signal;
								timestamp = new Timestamp(System.currentTimeMillis());
								consoleMessage="["+timestamp+"] "+Thread.currentThread().getName()+": Ricevuto "+signal+" ..continuo!";
								System.out.println(consoleMessage);
								position++;//17
								setChanged();
								notifyObservers(this);
								/**
								 * Sleep giusto per far vedere meglio il flusso di esecuzione da GUI 
								 */
								Thread.sleep(timeToSleep);


								/**
								 * se ricevo questa stringa vuol dire che lo slave ha ricevuto JOBREADY. posso uscire!
								 */
								break;
							}
						}
						break;
					} catch (IOException e) {
						System.err.println("..........");
						e.printStackTrace();
						System.exit(1);
					}
				}

				try {
					outSock.flush();
				} catch (IOException ioe) {
					System.err.println(Thread.currentThread().getName()+": Eccezione nella flush");
					ioe.printStackTrace();
				}


				/**
				 * Attendo eventualmente (nel caso ancora non fosse terminato) la terminazione del thread
				 * che invia gli rssi ai client rilevati dall'inquiry
				 */
				try {
					sendRssiThread.join(10000);

					try {
						sendRssiThread.interrupt();
					} catch (SecurityException se2) {
						se2.printStackTrace();
						System.err.println("Eccezione nella interrupt thread!");
						continue;
					}

				} catch (InterruptedException sie) {
					sie.printStackTrace();
					//continue;
					System.exit(10);
				}


				/**
				 * FINE. Ritorno all'inizio SE IN WHILE(TRUE)
				 */
				timestamp = new Timestamp(System.currentTimeMillis());
				consoleMessage="["+timestamp+"] MASTERJOBSERVERTHREAD: fine...se fossi nel while(true) dovrei ricominciare da capo! Sleep(2000)..   ^_^";
				position++;//18
				setChanged();
				notifyObservers(this);


				System.out.println(consoleMessage);

				//Scelta di sospendere per 5 secondi prima di ricominciare
				Thread.sleep(5000);

			} catch(Exception e) {
				/**
				 * Chiusura socket ed I/O stream
				 */	
				e.printStackTrace();
				System.err.println("MasterJobServerThread: Chiudo i canali I/O e la socket\n");
				setChanged();
				notifyObservers("MasterJobServerThread: Chiudo i canali I/O e la socket\n");

				try{
					inSock.close();
					outSock.close();
					masterJobServerSocket.close();
					slaveClientSocket.close();
					System.exit(99);
				} catch (Exception e2) {
					e2.printStackTrace();
				}  
			}

			//	ciclo = false;

		}//fine while(true)

	}//fine run()


	/**
	 * Metodo che filtra gli elementi di un file dato in ingresso scrivendoli su un file di uscita a seconda
	 * del parametro massimo di RSSI fornito in ingresso
	 * @param inquiryRssiThresholding Valore massimo di RSSI oltre il quale filtrare
	 * @param fileIn Path completo del file di ingresso
	 * @param fileOut Path completo del file di uscita
	 */
	public void filterInquiryWithThresholding(int inquiryRssiThresholding, String fileIn, String fileOut) {
		System.out.println("AVVIATO INQUIRY FILTERING WITH THRESHOLDING = "+getInquiryThresholding());
		StringTokenizer st = null;
		String delim = ":";
		boolean moreLines = true;	
		BufferedReader in = null;
		FileOutputStream out = null;
		PrintStream ps; 
		try {
			in = new BufferedReader(new FileReader(fileIn));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		try {
			out = new FileOutputStream(fileOut);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		ps = new PrintStream(out);
		while (moreLines) {
			//estrazione della linea
			String linea = LineUtility.getNextLine(in);

			if (linea.equals("Nessuna linea disponibile")) { 
				moreLines = false;
				break; //esco dal ciclo
			}

			st = new StringTokenizer(linea);
			while(st.hasMoreTokens()){
				try{
					String mac = st.nextToken(delim);	
					String rssi = st.nextToken(delim);
					if (Integer.parseInt(rssi) < getInquiryThresholding()) ps.println(":"+mac+":"+rssi+":");
				}catch(NoSuchElementException e) {
					continue;	
				}

			}//fine while(hasMoreTokens())	

		}//fine while(moreLines)

		try {
			ps.close();
			in.close();
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}//fine metodo filterInquiryWithThresholding

}//fine MasterJobServerThread
